package com.siyu.quartz.task;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.siyu.basedata.modelparam.domain.ModelParam;
import com.siyu.basedata.modelparam.service.IModelParamService;
import com.siyu.basedata.plan.domain.Plan;
import com.siyu.basedata.plan.service.IPlanService;
import com.siyu.basedata.wrprsrhych.domain.WrpRsrHych;
import com.siyu.basedata.wrprsrhych.service.IWrpRsrHychService;
import com.siyu.basedata.xajresult.domain.XajResult;
import com.siyu.basedata.xajresult.service.IXajResultService;
import com.siyu.common.constant.PredictConstants;
import com.siyu.common.constant.PrewarnConstants;
import com.siyu.common.utils.uuid.IdUtils;
import com.siyu.model.xajmodel.XajData;
import com.siyu.model.xajmodel.XajModel;
import com.siyu.predict.dto.RelCalcDto;
import com.siyu.predict.rsvrjob.domain.RsvrJob;
import com.siyu.predict.rsvrjob.service.IRsvrJobService;
import com.siyu.predict.service.PredictService;
import com.siyu.predict.utils.DataUtil;
import com.siyu.rehearsal.controller.PlanManageController;
import org.apache.commons.collections4.Put;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Component("calcTask")
public class CalcTask {
    @Autowired
    private IModelParamService modelParamService;
    @Autowired
    private IWrpRsrHychService wrpRsrHychService;
    @Autowired
    private IPlanService planService;
    @Autowired
    private IXajResultService xajResultService;
    @Autowired
    private StringRedisTemplate stringcalcRedisTemplate;
    @Autowired
    private IRsvrJobService rsvrJobService;
    @Autowired
    private PredictService predictService;

    @Transactional
    public void calc() throws Exception {
        List<RsvrJob> rsvrJobs = rsvrJobService.list(new LambdaQueryWrapper<RsvrJob>().eq(RsvrJob::getRemark, "calcTask-calc"));
        try {
            ValueOperations<String, String> ops = stringcalcRedisTemplate.opsForValue();
            Boolean lock = ops.setIfAbsent("xajcalc_lock", "xajcalclock", Duration.ofSeconds(600)); // 加锁
            // 获取当前时间
            Date startTime = DateUtil.parse(DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:00:00")).toJdkDate();
            if (lock != null && lock) {
                for (RsvrJob rsvrJob : rsvrJobs) {
                    // 获取未来时间
                    DateTime endTime = DateUtil.offsetHour(startTime, rsvrJob.getPreTm());
                    // 获取面雨量
                    HashMap<Object, Object> areaRainfall = DataUtil.getpreAreaRainfall(rsvrJob.getRscd(), startTime, endTime, 1);
                    List<Double> areaRain = Convert.toList(Double.class, areaRainfall.get("areaRain"));
                    // 蒸发序列
                    List<Double> erList = IntStream.range(0, areaRain.size()).mapToObj(i -> 0.0).collect(Collectors.toList());
                    // 获取模型参数
                    QueryWrapper<ModelParam> modelParamQueryWrapper = new QueryWrapper<>();
                    modelParamQueryWrapper.eq("model_mark", "xaj").eq("rscd",rsvrJob.getRscd());
                    Map<String, Double> map = modelParamService.list(modelParamQueryWrapper).stream()
                            .collect(Collectors.toMap(ModelParam::getParamNum, ModelParam::getParamDefault, (oldValue, newValue) -> oldValue, HashMap::new));
                    QueryWrapper<WrpRsrHych> wrpRsrHychQueryWrapper = new QueryWrapper<>();
                    wrpRsrHychQueryWrapper.eq("rscd", rsvrJob.getRscd());
                    WrpRsrHych wrpRsrHych = wrpRsrHychService.getOne(wrpRsrHychQueryWrapper);
                    XajData xajData = new XajData(areaRain, erList, 1, wrpRsrHych.getCnctar(), map);
                    // 开始新安江模型计算
                    Map<Object, Object> calc = XajModel.calc(xajData);
                    // 获取模型计算结果
                    List<Double> preQ = Convert.toList(Double.class, calc.get("preQ")); // 预报流量
                    List<Double> netRain = Convert.toList(Double.class, calc.get("netRain")); // 净雨量
                    List<String> tms = Convert.toList(String.class, areaRainfall.get("tms")); // 时间序列
                    // 预报水位
                    List<Double> regulate = DataUtil.regulate(preQ, tms, rsvrJob.getRscd());
                    List<Double> preZ = DataUtil.waterBalance(preQ, regulate, tms, rsvrJob.getRscd());
                    List<XajResult> xajResults = new ArrayList<>();
                    // 数据结果处理
                    for (int i = 0; i < areaRain.size(); i++) {
                        XajResult xajResult = new XajResult();
                        xajResult.setTm(tms.get(i));
                        xajResult.setPreQ(preQ.get(i));
                        xajResult.setNetRain(netRain.get(i));
                        xajResult.setAreaRain(areaRain.get(i));
                        // TODO: 2024/6/16 需要替换为真实的预测水位和校正流量
//                        xajResult.setCheckQ(preQ.get(i) + NumberUtil.round(Math.random() * 10, 2).doubleValue());
                        xajResult.setPreZ(preZ.get(i));
                        xajResults.add(xajResult);
                    }

                    // 保存方案
                    try {
                        Plan plan = new Plan();
                        String planId = IdUtils.getId();
                        plan.setId(planId);
                        plan.setRscd(rsvrJob.getRscd());
                        plan.setPlanName("自动预报" + DateUtil.format(new Date(), "yyyyMMddHHmmss"));
                        plan.setPlanType("自动");
                        plan.setCalcType("自动计算");
                        plan.setStartTime(startTime);
                        plan.setEndTime(endTime);
                        plan.setStep(1);
                        plan.setRunoffNum(Double.valueOf(calc.get("runoffNum").toString()));
                        plan.setPlanDesc(DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:00:00") + "自动计算任务");
                        plan.setPreType("预报");
                        plan.setCreateBy("系统");
                        plan.setCreateTime(new Date());
                        planService.save(plan);
                        // 保存方案是数据
                        // 保存预报结果
                        List<XajResult> list = xajResults.stream()
                                .map(xajResult -> {
                                    xajResult.setPlanId(planId);
                                    xajResult.setId(IdUtils.getId());
                                    return xajResult;
                                })
                                .collect(Collectors.toList());
                        xajResultService.saveBatch(list);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
//                stringcalcRedisTemplate.delete("xajcalc_lock");
            }
        } catch (Exception e) {
//            stringcalcRedisTemplate.delete("xajcalc_lock");
            throw new RuntimeException(e);
        }
    }

    @Transactional
    public void xajcalc(String rscd) throws Exception {
        List<RsvrJob> rsvrJobs = rsvrJobService.list(new LambdaQueryWrapper<RsvrJob>().eq(RsvrJob::getRemark, "calcTask-calc").eq(RsvrJob::getRscd, rscd));
        try {
            ValueOperations<String, String> ops = stringcalcRedisTemplate.opsForValue();
            Boolean lock = ops.setIfAbsent(rscd+"xajcalc_lock", "xajcalclock", Duration.ofSeconds(600)); // 加锁
            // 获取当前时间
            Date startTime = DateUtil.parse(DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:00:00")).toJdkDate();
            if (lock != null && lock) {
                for (RsvrJob rsvrJob : rsvrJobs) {
                    RelCalcDto relCalcDto = new RelCalcDto();
                    relCalcDto.setRscd(rsvrJob.getRscd());
                    relCalcDto.setPreinterval(rsvrJob.getPreTm());
                    // 开始计算
                    Map<Object, Object> map = predictService.relCalc(relCalcDto);
                    // 获取未来时间
                    DateTime endTime = DateUtil.offsetHour(startTime, rsvrJob.getPreTm());
                    Object xajData = map.get("xajData");
                    List<XajResult> xajResults = (List<XajResult>) xajData;
                    Map<String, Object> countsMap = (Map<String, Object>) map.get("counts");
                    Double runoffNum = ((Number) countsMap.get("runoffNum")).doubleValue();
                    QueryWrapper<WrpRsrHych> wrpRsrHychQueryWrapper = new QueryWrapper<>();
                    wrpRsrHychQueryWrapper.eq("rscd", rsvrJob.getRscd());
                    WrpRsrHych wrpRsrHych = wrpRsrHychService.getOne(wrpRsrHychQueryWrapper);
                    // 判断是否有预警
                    Optional<XajResult> firstExceedingElement = xajResults.stream()
                            .filter(xajResult -> xajResult.getPreZ() > wrpRsrHych.getMjflsscnwl())
//                            .filter(xajResult -> xajResult.getPreZ() > 20)
                            .findFirst();
                    XajResult result = firstExceedingElement.orElse(null);
                    if (result != null) {
                        HashMap<String, HashMap<String, Object>> map1 = new HashMap<>();
                        HashMap<String, Object> map2 = new HashMap<>();
                        map2.put("tm", result.getTm());
                        map2.put("preZ", result.getPreZ());
                        map1.put("floodLimit",map2);
                        // 设计洪水位
                        Optional<XajResult> firstExceedingElement2 = xajResults.stream()
                                .filter(xajResult -> xajResult.getPreZ() > wrpRsrHych.getDsfllv())
//                                .filter(xajResult -> xajResult.getPreZ() > 30)
                                .findFirst();
                        XajResult result2 = firstExceedingElement2.orElse(null);
                        if (result2 != null) {
                            HashMap<String, Object> map3 = new HashMap<>();
                            map3.put("tm", result.getTm());
                            map3.put("preZ", result.getPreZ());
                            map1.put("design",map3);
                            // 校核洪水位
                            Optional<XajResult> firstExceedingElement3 = xajResults.stream()
                                    .filter(xajResult -> xajResult.getPreZ() > wrpRsrHych.getChfllv())
//                                    .filter(xajResult -> xajResult.getPreZ() > 40)
                                    .findFirst();
                            XajResult result3 = firstExceedingElement3.orElse(null);
                            if (result3 != null) {
                                HashMap<String, Object> map4 = new HashMap<>();
                                map4.put("tm", result.getTm());
                                map4.put("preZ", result.getPreZ());
                                map1.put("check",map4);
                            }
                        }
                        // TODO: 2024/10/14 需要调用接口
                        String urlAds = PrewarnConstants.getUrlAds();
                        HashMap<String, Object> requestBody = new HashMap<>();
                        String json = JSONUtil.toJsonStr(map1);
                        requestBody.put("rscd",rscd);
                        requestBody.put("warnMes",json);
                        String body = HttpRequest.post(urlAds).form(requestBody).execute().body();
                    }

//                    // 获取面雨量
//                    HashMap<Object, Object> areaRainfall = DataUtil.getpreAreaRainfall(rsvrJob.getRscd(), startTime, endTime, 1);
//                    List<Double> areaRain = Convert.toList(Double.class, areaRainfall.get("areaRain"));
//                    // 蒸发序列
//                    List<Double> erList = IntStream.range(0, areaRain.size()).mapToObj(i -> 0.0).collect(Collectors.toList());
//                    // 获取模型参数
//                    QueryWrapper<ModelParam> modelParamQueryWrapper = new QueryWrapper<>();
//                    modelParamQueryWrapper.eq("model_mark", "xaj").eq("rscd",rsvrJob.getRscd());
//                    Map<String, Double> map = modelParamService.list(modelParamQueryWrapper).stream()
//                            .collect(Collectors.toMap(ModelParam::getParamNum, ModelParam::getParamDefault, (oldValue, newValue) -> oldValue, HashMap::new));
//                    QueryWrapper<WrpRsrHych> wrpRsrHychQueryWrapper = new QueryWrapper<>();
//                    wrpRsrHychQueryWrapper.eq("rscd", rsvrJob.getRscd());
//                    WrpRsrHych wrpRsrHych = wrpRsrHychService.getOne(wrpRsrHychQueryWrapper);
//                    XajData xajData = new XajData(areaRain, erList, 1, wrpRsrHych.getCnctar(), map);
//                    // 开始新安江模型计算
//                    Map<Object, Object> calc = XajModel.calc(xajData);
//                    // 获取模型计算结果
//                    List<Double> preQ = Convert.toList(Double.class, calc.get("preQ")); // 预报流量
//                    List<Double> netRain = Convert.toList(Double.class, calc.get("netRain")); // 净雨量
//                    List<String> tms = Convert.toList(String.class, areaRainfall.get("tms")); // 时间序列
//                    // 预报水位
//                    List<Double> regulate = DataUtil.regulate(preQ, tms, rsvrJob.getRscd());
//                    List<Double> preZ = DataUtil.waterBalance(preQ, regulate, tms, rsvrJob.getRscd());
//                    List<XajResult> xajResults = new ArrayList<>();
//                    // 数据结果处理
//                    for (int i = 0; i < areaRain.size(); i++) {
//                        XajResult xajResult = new XajResult();
//                        xajResult.setTm(tms.get(i));
//                        xajResult.setPreQ(preQ.get(i));
//                        xajResult.setNetRain(netRain.get(i));
//                        xajResult.setAreaRain(areaRain.get(i));
//                        xajResult.setPreZ(preZ.get(i));
//                        xajResults.add(xajResult);
//                    }
                    // 判断该方案是否存在

                    // 保存方案
                    try {
                        Plan plan = new Plan();
                        String planId = IdUtils.getId();
                        plan.setId(planId);
                        plan.setRscd(rsvrJob.getRscd());
                        plan.setPlanName("自动预报" + DateUtil.format(new Date(), "yyyyMMddHHmmss"));
                        plan.setPlanType("自动");
                        plan.setCalcType("自动计算");
                        plan.setStartTime(startTime);
                        plan.setEndTime(endTime);
                        plan.setStep(1);
                        plan.setRunoffNum(runoffNum);
                        plan.setPlanDesc(DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:00:00") + "自动计算任务");
                        plan.setPreType("预报");
                        plan.setCreateBy("系统");
                        plan.setCreateTime(new Date());
                        planService.save(plan);
                        // 保存预报结果
                        List<XajResult> list = xajResults.stream()
                                .map(xajResult -> {
                                    xajResult.setPlanId(planId);
                                    xajResult.setId(IdUtils.getId());
                                    return xajResult;
                                })
                                .collect(Collectors.toList());
                        xajResultService.saveBatch(list);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}
